"""
@File    :  dhcp_sniff_thread.py
@Author  :  LeeSen
@Date    :  2024-08-05
@Version :  Python 3.7.4
"""
import re

from PyQt5.QtCore import QThread, pyqtSignal
from scapy.layers.l2 import Ether, ARP
from scapy.sendrecv import sniff
from scapy.layers.dhcp import DHCP, BOOTP
from my_dhcp_client import DhcpEvent, DhcpOptionsDTO, DhcpPackageType, ArpPackageType, DhcpMessageDTO
from scapy_function import send_arp_backup


class DhcpSniffThread(QThread):
    pkt_signal = pyqtSignal(dict)

    def __init__(self,
                 nic,
                 message_sharing_queues,
                 dhcp_client_dict,
                 host_mac_to_ip_dict):
        super().__init__()
        self.stop_flag = False
        self.filter_des = "ip or arp"
        self.nic = nic
        self.message_sharing_queues = message_sharing_queues
        self.dhcp_client_dict = dhcp_client_dict
        self.host_mac_to_ip_dict = host_mac_to_ip_dict

    def run(self):
        res = {"code": "200", "message": "DHCP Sniff Thread will Start", "data": ""}
        self.pkt_signal.emit(res)
        try:
            sniff(filter=self.filter_des,
                  iface=self.nic,
                  stop_filter=lambda x: self.stop_flag,
                  prn=lambda pkt: self.package_handler(pkt))
        except:
            res["code"] = "404"
            res["message"] = "DHCP Sniff Thread have Except"
            self.pkt_signal.emit(res)
        res["code"] = "200"
        res["message"] = "DHCP Sniff Thread will Stop"
        self.pkt_signal.emit(res)

    def stop(self):
        self.stop_flag = True

    def package_handler(self, pkt):
        item = pkt[0]
        if not item[Ether].src in self.host_mac_to_ip_dict.keys():
            if item[Ether].type == 2048 and item.haslayer("BOOTP"):
                host_mac = re.sub(r"(?<=\w)(?=(?:\w\w)+$)", ":", item[BOOTP].chaddr.hex()[:12])
                if item.dport == 68 and item[BOOTP].xid == self.dhcp_client_dict[host_mac].transaction_id:
                    current_event = DhcpEvent.NULL
                    dhcp_options_message = DhcpOptionsDTO()
                    dhcp_options_message.transaction_id = item[BOOTP].xid
                    dhcp_options_message.client_ip = item[BOOTP].yiaddr
                    dhcp_options_message.server_mac = item[Ether].src
                    for option in item[DHCP].options:
                        if isinstance(option, tuple):
                            if option[0] == "message-type":
                                dhcp_options_message.message_type = option[1]
                            elif option[0] == "server_id":
                                dhcp_options_message.server_ip = option[1]
                            elif option[0] == "lease_time":
                                dhcp_options_message.lease_time = option[1]
                            elif option[0] == "name_server":
                                dhcp_options_message.name_server = option[1]
                            elif option[0] == "subnet_mask":
                                dhcp_options_message.subnet_mask = option[1]
                            elif option[0] == "router":
                                dhcp_options_message.router = option[1]
                            elif option[0] == "domain":
                                dhcp_options_message.domain = option[1]
                            else:
                                pass
                    if dhcp_options_message.message_type == DhcpPackageType.OFFER.value:
                        current_event = DhcpEvent.OFFER_RECEIVED
                    elif dhcp_options_message.message_type == DhcpPackageType.ACK.value:
                        current_event = DhcpEvent.ACK

                    elif dhcp_options_message.message_type == DhcpPackageType.NACK.value:
                        current_event = DhcpEvent.NACK
                    else:
                        pass
                    dhcp_message = DhcpMessageDTO(host_mac, dhcp_options_message, current_event)
                    self.message_sharing_queues.put(dhcp_message)

            elif item[Ether].type == 2054:
                if item[ARP].op == ArpPackageType.REQUEST.value:
                    if item[ARP].pdst in self.host_mac_to_ip_dict.values():
                        for k, v in self.host_mac_to_ip_dict.items():
                            if v == item[ARP].pdst:
                                send_arp_backup(ArpPackageType.REPLY.value, item[Ether].src,
                                                k, k, item[ARP].pdst,
                                                item[ARP].hwsrc, item[ARP].psrc, self.nic)

                        pass
                elif item[ARP].op == ArpPackageType.REPLY.value:

                    if item.psrc in self.host_mac_to_ip_dict.values():
                        current_event = DhcpEvent.ARP_RECEIVED
                        dhcp_options_message = DhcpOptionsDTO()

                        for k, v in self.host_mac_to_ip_dict.items():
                            if item.psrc == v:
                                transaction_id = k
                                dhcp_options_message.transaction_id = transaction_id
                                dhcp_options_message.client_ip = item.psrc
                                dhcp_message = DhcpMessageDTO(k, dhcp_options_message, current_event)
                                self.message_sharing_queues.put(dhcp_message)

                                break

                else:
                    pass
            else:
                pass

        else:
            pass